//
// Created by 孙凌霄 on 2021/6/30.
//

#include "EasyPlayer.h"
#include <cstring>
#include <pthread.h>
#include "macro.h"

// 解析视频资源任务
void *task_prepare(void *args) {
    EasyPlayer *easyPlayer = static_cast<EasyPlayer *>(args);

    easyPlayer->_prepare();
    return nullptr;
}

EasyPlayer::EasyPlayer(JavaCallHelper *javaCallHelper, const char *dataSource) {
    // 此处由jni传来的string，jni方法使用完毕后会释放资源
    // 但是此处的dataSource要保留，所以需要进行一次内存拷贝
    this->dataSource = new char[strlen(dataSource)];
    this->javaCallHelper = javaCallHelper;
    strcpy(this->dataSource, dataSource);
}

EasyPlayer::~EasyPlayer() {
    // 释放资源
    DELETE(dataSource)
    DELETE(javaCallHelper)
}

void EasyPlayer::prepare() {
    pthread_create(&pid, nullptr, task_prepare, this);
}

void EasyPlayer::_prepare() {
    // 初始化网络，让ffmpeg能使用网络
    avformat_network_init();

    formatContext = nullptr;

    // 1.打开媒体文件(文件地址、直播地址) 0成功，非0失败
    int mediaResult = avformat_open_input(&formatContext, dataSource, nullptr, nullptr);
    if (mediaResult != 0) {
        // 打开失败：文件路径不对、手机网络不通等
        // 使用反射回调到Java层
        LOGE("open url error:%s", av_err2str(mediaResult)); // 将错误码转成string
        javaCallHelper->onError(THREAD_CHILD, FFMPEG_CAN_NOT_OPEN_URL);
        return;
    }

    // 2.查找媒体中的音视频流,给formatContext中的成员赋值
    mediaResult = avformat_find_stream_info(formatContext, nullptr);
    // 小于0则失败
    if (mediaResult < 0) {
        LOGE("find stream error:%s", av_err2str(mediaResult));
        javaCallHelper->onError(THREAD_CHILD, FFMPEG_CAN_NOT_FIND_STREAMS);
        return;
    }

    // 循环获取的流，分别处理
    for (int i = 0; i < formatContext->nb_streams; ++i) {
        // 获取流：可能是音频也可能是视频
        AVStream *stream = formatContext->streams[i];

        // 流的参数信息：宽高、码率、帧率等
        AVCodecParameters *codecParameters = stream->codecpar;

        // 通过当前流使用的编码方式，查找解码器：无论音频还是视频都需要获得解码器
        AVCodec *dec = avcodec_find_decoder(codecParameters->codec_id);
        if (dec == nullptr) {
            LOGE("find codec fail:%s", av_err2str(mediaResult));
            javaCallHelper->onError(THREAD_CHILD, FFMPEG_FIND_DECODER_FAIL);
            return;
        }

        // 获取解析器的上下文
        AVCodecContext *context = avcodec_alloc_context3(dec);
        if (context == nullptr) {
            LOGE("create codec context fail:%s", av_err2str(mediaResult));
            javaCallHelper->onError(THREAD_CHILD, FFMPEG_ALLOC_CODEC_CONTEXT_FAIL);
            return;
        }

        // 设置上下文的参数
        mediaResult = avcodec_parameters_to_context(context, codecParameters);

        if (mediaResult < 0) {
            LOGE("set codec context params fail:%s", av_err2str(mediaResult));
            javaCallHelper->onError(THREAD_CHILD, FFMPEG_CODEC_CONTEXT_PARAMETERS_FAIL);
            return;
        }

        // 打开解码器
        mediaResult = avcodec_open2(context, dec, nullptr);
        if (mediaResult != 0) {
            LOGE("open codec fail:%s", av_err2str(mediaResult));
            javaCallHelper->onError(THREAD_CHILD, FFMPEG_OPEN_DECODER_FAIL);
            return;
        }

        // 音视频分别交给不同的类处理
        if (codecParameters->codec_type == AVMEDIA_TYPE_AUDIO) {
            // 音频
            audioChannel = new AudioChannel;
        } else if (codecParameters->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoChannel = new VideoChannel;
        }
    }

    // 处理了一番没有音视频
    if (!audioChannel && !videoChannel) {
        LOGE("no media");
        javaCallHelper->onError(THREAD_CHILD, FFMPEG_NOMEDIA);
        return;
    }

    // 准备完毕，通知Java层开始播放
    javaCallHelper->onPrepare(THREAD_CHILD);
}